package maa.database;

import maa.util.PropertyManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConnectionPool implements Pool<Connection> {
    private static final Logger logger = LoggerFactory.getLogger(ConnectionPool.class);
    private static ConnectionPool instance = null;
    private static Lock lock = new ReentrantLock(true);
    private final int MAX_WAITING_TIME = 2;
    private BlockingQueue<Connection> connections;

    private ConnectionPool() {
        initConnections();
    }

    public static ConnectionPool getInstance() {
        lock.lock();
        if (null == instance) instance = new ConnectionPool();
        lock.unlock();
        return instance;
    }

    private final void initConnections() {
        PropertyManager propertyManager = new PropertyManager("db.properties");
        Integer poolSize = Integer.valueOf(propertyManager.getProperty("poolSize"));
        String url = propertyManager.getProperty("url");
        String login = propertyManager.getProperty("login");
        String password = propertyManager.getProperty("password");
        String driverName = propertyManager.getProperty("driverName");
        connections = new LinkedBlockingQueue<Connection>(poolSize);
        try {
            Class.forName(driverName);
        } catch (ClassNotFoundException e) {
            logger.debug(e.getMessage());
        }
        for (int i = 0; i < poolSize; i++) {
            Connection connection = null;
            try {
                connection = DriverManager.getConnection(url, login, password);
            } catch (SQLException e) {
                logger.debug("Could not initialize connection pool!\n" + e.getMessage());
            }
            connections.add(connection);
        }
    }

    @Override
    public Connection getConnection() {
        try {
            Connection connection = connections.poll(MAX_WAITING_TIME, TimeUnit.SECONDS);
            if (connection != null) {
                logger.debug("Connection " + connection + " took from connection pool");
            } else {
                logger.error("Couldn't retrieve a connection from pool");
            }
            return connection;
        } catch (InterruptedException e) {
            logger.error("Could not get connection! " + e.getMessage());
            return null;
        }
    }

    public final void release(Connection connection) {
        if (connection != null) {
            try {
                connections.put(connection);
                logger.debug("Connection " + connection + " returned to connection pool");
                logger.debug("There are(is) " + (connections.size() - connections.remainingCapacity()) + " connection(s) in the pool.");
            } catch (InterruptedException e) {
                logger.error("Could not release connection into the pool " + e.getMessage());
            }
        }
    }

}
